package org.techism.server.dao.impl;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.techism.client.bo.Event;
import org.techism.client.bo.EventAttributes;
import org.techism.server.dao.EventDao;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.Link;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.repackaged.com.google.common.base.StringUtil;

public class EventDaoImpl implements EventDao {

	private static final Logger log = Logger.getLogger(EventDaoImpl.class.getName());

	private DatastoreService datastoreService = DatastoreServiceFactory.getDatastoreService();

	@Override
	public Event loadByEventId(final String eventId) {
		final Query query = new Query("Event");
		query.addFilter(EventAttributes.EVENTID.toString(), FilterOperator.EQUAL, eventId);
		final Entity eventEntity = datastoreService.prepare(query).asSingleEntity();
		if (eventEntity != null) {
			return getEvent(eventEntity);
		} else {
			return null;
		}
	}

	@Override
	public SortedMap<String, List<Event>> loadFutureEventsForCurrentYear() {
		return loadFutureEventsForCurrentYear(null);
	}

	@Override
	public SortedMap<String, List<Event>> loadFutureEventsForCurrentYear(final String tag) {
		final Query query = new Query("Event");
		final Date atm = new Date();
		final GregorianCalendar calendar = new GregorianCalendar();
		calendar.setTime(atm);
		return loadEventsUntilEndOfYear(calendar, tag);
	}

	@Override
	public SortedMap<String, List<Event>> loadByYear(final int year) {
		return loadByTagAndYear(year, null);
	}

	@Override
	public SortedMap<String, List<Event>> loadByTagAndYear(final int year, final String tag) {
		final GregorianCalendar calendar = new GregorianCalendar(year, Calendar.JANUARY, 1);
		return loadEventsUntilEndOfYear(calendar, tag);
	}

	@Override
	public SortedMap<String, Integer> findAllTags() {
		final Query query = new Query("Event");
		return performTagQuery(query);
	}

	@Override
	public SortedMap<String, Integer> loadTagsForFutureEventsForCurrentYear() {
		final Query query = new Query("Event");
		query.addFilter(EventAttributes.FROM.toString(), FilterOperator.GREATER_THAN_OR_EQUAL, new Date());
		return performTagQuery(query);
	}

	private SortedMap<String, List<Event>> loadEventsUntilEndOfYear(Calendar calendar, String tag) {
		final Query query = new Query("Event");
		query.addFilter(EventAttributes.FROM.toString(), FilterOperator.GREATER_THAN_OR_EQUAL, calendar
				.getTime());
		final GregorianCalendar calendar2 = new GregorianCalendar(calendar.get(Calendar.YEAR) + 1,
				Calendar.JANUARY, 1);
		query.addFilter(EventAttributes.FROM.toString(), FilterOperator.LESS_THAN, calendar2.getTime());
		if (!StringUtil.isEmptyOrWhitespace(tag)) {
			query.addFilter(EventAttributes.TAGS.toString(), FilterOperator.EQUAL, tag);
		}
		return performQuery(query);
	}

	private SortedMap<String, List<Event>> performQuery(final Query query) {
		final SortedMap<String, List<Event>> events = new TreeMap<String, List<Event>>();
		for (final Entity eventEntity : datastoreService.prepare(query).asIterable()) {
			final Event event = getEvent(eventEntity);
			final Date from = event.getFrom();
			final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
			final String date = formatter.format(from);
			if (events.get(date) != null) {
				final List<Event> eventList = events.get(date);
				eventList.add(event);
			} else {
				final List<Event> eventList = new ArrayList<Event>();
				eventList.add(event);
				events.put(date, eventList);
			}
		}
		return events;
	}

	private SortedMap<String, Integer> performTagQuery(Query query) {
		final SortedMap<String, Integer> result = new TreeMap<String, Integer>();
		for (final Entity taskEntity : datastoreService.prepare(query).asIterable()) {
			try {
				final Entity entity = datastoreService.get(taskEntity.getKey());
				final List<String> tags = (List<String>) entity.getProperty(EventAttributes.TAGS.toString());
				if (tags != null) {
					for (final String tag : tags) {
						if (result.get(tag) == null) {
							result.put(tag, 1);
						} else {
							result.put(tag, result.get(tag) + 1);
						}
					}
				}
			} catch (final EntityNotFoundException ex) {
				log.log(Level.SEVERE, ex.getMessage(), ex);
			}
		}
		return result;
	}

	private Event getEvent(final Entity entity) {
		final Event event = new Event();
		final String eventId = (String) entity.getProperty(EventAttributes.EVENTID.toString());
		event.setEventId(eventId);
		final String title = (String) entity.getProperty(EventAttributes.TITLE.toString());
		event.setTitle(title);
		final String description = (String) entity.getProperty(EventAttributes.DESCRIPTION.toString());
		event.setDescription(description);
		final Date from = (Date) entity.getProperty(EventAttributes.FROM.toString());
		event.setFrom(from);
		final Date until = (Date) entity.getProperty(EventAttributes.UNTIL.toString());
		event.setUntil(until);
		final Link url = (Link) entity.getProperty(EventAttributes.URL.toString());
		if (url != null) {
			event.setUrl(url.getValue());
		}
		final String organizer = (String) entity.getProperty(EventAttributes.ORGANIZER.toString());
		event.setOrganizer(organizer);
		final Key organizerKey = (Key) entity.getProperty(EventAttributes.GROUPID.toString());
		if (organizerKey != null) {
			event.setGroupId(organizerKey.getId());
		}
		final List<String> tags = (List<String>) entity.getProperty(EventAttributes.TAGS.toString());
		event.setTags(tags);
		return event;
	}

}
